package org.codetranslators.common;

import java.util.HashMap;
import java.util.Vector;

/*
 * The DiGraph class represents a directed graph characterized by successor and predecessor nodes.
 * In compilations, control flow graphs are directed graphs.
 */
public class DiGraph
{
	private Vector<Node> vertices;          // List of graph nodes
	private Vector<Edge> edges;
	private HashMap<Node, Integer> nodesVsOrderNumber;
	private int nodeCount;

	public DiGraph()
	{
		vertices = new Vector<Node>();
		edges = new Vector<Edge>();

		nodesVsOrderNumber = new HashMap<Node, Integer>();
		nodeCount = 0;
	}

	public void initNodes(int numNodes){
		vertices = new Vector<Node>(numNodes);
		for(int i = 0; i < numNodes; i++){
			vertices.add(null);
		}
	}

	public Vector getOutgoingEdges(Node node){
		return node.getOutgoingEdges();
	}

	public int getNumNodes(){
		return vertices.size();
	}

	public int getNumEdges(){
		return edges.size();
	}

	public Node getNode(int index){
		return (Node) vertices.elementAt(index);
	}

	public Node nextNode(Node currentNode)
	{
		for(int i = 0; i < getNumNodes(); i++)
		{
			if (getNode(i) == currentNode)
			{
				if (i < getNumNodes() - 1)
					return getNode(++i);
				return null;
			}
		}
		return null;
	}

	public Edge getEdge(int index){
		return (Edge) edges.elementAt(index);
	}

	public Node getOppositeNode(Node node, Edge edge){
		return edge.getOppositeNode(node);
	}

	public void addNode(Node node){
		vertices.addElement(node);
		int nodeNum = vertices.size();
		node.setPositionInGraph(nodeNum);
	}

	public void addNodeAt(Node node, int index){
		vertices.setElementAt(node, index);
		node.setPositionInGraph(index);
	}

	public void addDirectedEdge(Edge edge, Node src, Node dest) {
		edges.addElement(edge);
		edge.setDirected(true);
		int edgeNum = edges.size() -1;
		edge.setPositionInGraph(edgeNum);
		edge.setDestination(dest);
		edge.setSrc(src);

		src.addSuccessesor(dest);
		// Assign a number to the destination node
		if(nodeCount == 0){
			nodesVsOrderNumber.put(src, new Integer(nodeCount));
		}
		else{
			nodeCount++;
			nodesVsOrderNumber.put(dest, new Integer(nodeCount));
		}

		// Add the predecessor to the destination at the right index
		Vector<Node> predsOfDest = dest.getPredecessors();
		if(predsOfDest == null && predsOfDest.size() > 0 )  // No predecessor yet
		{
			dest.addPredecessor(src);
		}
		else{
			int numPredsOfDest = predsOfDest.size();
			Integer orderIntOfSrc = nodesVsOrderNumber.get(src);
			int numOrderOfSrc = orderIntOfSrc.intValue();

			int indexToInsertPred = 0;
			for(int i = 0; i < numPredsOfDest; i++){
				Node predOfDest = predsOfDest.elementAt(i);
				Integer orderInt = nodesVsOrderNumber.get(predOfDest);
				if(orderInt != null){
					int orderNumOfPred = orderInt.intValue();
					if(numOrderOfSrc < orderNumOfPred){
						indexToInsertPred = i -1;
						break;
					}
				}
			}
			if(indexToInsertPred < 0) indexToInsertPred = 0;
			dest.addPredecessorAt(src, indexToInsertPred);
		}

		src.addOutgoingEdge(edge);
		dest.addIncomingEdge(edge);
	}

	public void display(){
		for(int i = 0; i < vertices.size(); i++) {
			Node node = (Node)vertices.elementAt(i);
			if(node == null) continue;
			System.out.print("Node " + node.getName() + " has edges to : ");
			System.out.println("edges----"+node.getName()+"----incoming edges--"+node.getNumIncomingEdges()+"--outgoing edges---"+node.getOutgoingEdges()+"--postion in graph--"+node.getPositionInGraph()+"-successors size--"+node.getSuccessors().size()+"position in the graph"+node.getPositionInGraph());
			Vector succs = node.getSuccessors();
			for(int j = 0; j < succs.size(); j++){
				Node suc = (Node) succs.elementAt(j);
				System.out.print(suc.getName() + ", ");
			}
			System.out.println("");
		}
	}
}
